﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

public class UnityAwaiter : IAwaiter<UnityAwaiter, UnityAwaiter>
{
    public bool IsCompleted { get; set; }

    protected Action continuation = null;

    public UnityAwaiter()
    {
    }

    public UnityAwaiter GetAwaiter()
    {
        return this;
    }

    public UnityAwaiter GetResult()
    {
        return this;
    }

    public virtual void OnCompleted(Action continuation)
    {
        this.continuation += continuation;
    }

    /// <summary>
    /// 每帧调用  返回 true 继续执行 false 则表示结束释放
    /// </summary>
    public virtual bool MoveNext()
    {
        continuation?.Invoke();
        continuation = null;
        return false;
    }

}

public class WaitUntilAwaiter : UnityAwaiter
{
    public Func<bool> func { get; private set; }
    public WaitUntilAwaiter(Func<bool> func)
    {
        this.func = func;
    }
    public override bool MoveNext()
    {
        if (!func())
            return true;
        return base.MoveNext();
    }
}

public abstract class CoroutineAwaiter : UnityAwaiter
{
    public abstract IEnumerator GetEnumerator();
    protected IEnumerator _itor;
    public CoroutineAwaiter()
    {
        _itor = GetEnumerator();
    }
    public override bool MoveNext()
    {
        if (_itor != null && _itor.MoveNext())
        {
            return true;
        }
        return base.MoveNext();
    }
}

public class WaitDelayFrameAwaiter : CoroutineAwaiter
{
    public int delayNum { get; private set; }
    public WaitDelayFrameAwaiter(int delayNum)
    {
        this.delayNum = delayNum;
    }
    public override IEnumerator GetEnumerator()
    {
        var num = 0;
        while (num++ < delayNum)
            yield return null;
    }
}

/* Unity 的 WaitForSeconds 可能不适用  会导致延迟时间不足
*/
public class WaitForCoroutine : CoroutineAwaiter
{
    public LinkedList<IEnumerator> taskList = new LinkedList<IEnumerator>();

    public float duration = -1;
    // 一帧内最多调用几次
    public int frameRunNum = -1;
    private int m_lastFrame, m_frameNum;

    public WaitForCoroutine(IEnumerator itor)
    {
        taskList.AddFirst(itor);
    }
    public override IEnumerator GetEnumerator()
    {
        return null;
    }
    public override bool MoveNext()
    {
        bool useFrame = frameRunNum > -1;
        bool useDt = duration > -1;
        if (useFrame)
        {
            if (m_lastFrame == UnityEngine.Time.frameCount)
            {
                if (m_frameNum <= 0)
                    return true;
                m_frameNum--;
            }
            else
            {
                m_frameNum = frameRunNum;
                m_lastFrame = UnityEngine.Time.frameCount;
            }
        }
        //UnityEngine.Debug.Log($"[Cat] ? n:{frameRunNum}, f:{m_lastFrame}, {m_frameNum}, fc:{UnityEngine.Time.frameCount}");
        DateTime st = DateTime.Now;
        while (taskList.Count > 0)
        {
            var current = taskList.First.Value;
            if (current.MoveNext())
            {
                if (current.Current is IEnumerator)
                {
                    taskList.AddFirst(current.Current as IEnumerator);
                }
            }
            else
            {
                taskList.RemoveFirst();
            }
            //Debug.LogFormat("[Update] : {0}, {1}, {2}", sf, taskList.Count, dt);
            if (useFrame || (useDt && (DateTime.Now - st).TotalSeconds > duration))
                break;
        }
        if (taskList.Count == 0)
        {
            continuation?.Invoke();
        }
        return taskList.Count > 0;
    }
}

public class DelayAwaiter : CoroutineAwaiter
{
    public float delay { get; private set; }
    public DelayAwaiter(float delay)
    {
        this.delay = delay;
    }
    public override IEnumerator GetEnumerator()
    {
        var st = UnityEngine.Time.time;
        while (UnityEngine.Time.time - st < delay)
            yield return null;
    }
}
